//
//  bleCentralManager.m
//  MonitoringCenter
//
//  Created by David ding on 13-1-10.
//
//
#import <UIKit/UIKit.h>
#import "BLECentralManager.h"
#import "BLEPeripheral.h"

//mBot重命名归档路径
#define ARCHIVE_PATH_RENAME (@"identifierAndAliasDic.plist")

@interface BLECentralManager ()<CBCentralManagerDelegate,BLEPeripheralDelegate>

#pragma mark -- private
//中心设备管理
@property(strong, nonatomic) CBCentralManager *activeCentralManager;
//代理
@property (nonatomic, strong) NSMutableArray<id<BLEControllerDelegate>> *delegates;
//peripheral的identifier和alias的字典  k=identifier  v=alias
@property(strong, nonatomic) NSMutableDictionary *identifierAndAliasDic;

#pragma mark -- readonly
//扫描到的外围设备
@property(strong, nonatomic) NSMutableArray<BLEPeripheral *> *peripherals;
//当前连接的蓝牙设备名称
@property(strong, nonatomic) NSString *currentConnectedDeviceName;
//中心设备状态
@property (nonatomic,assign) BLECentralDelegateState currentCentralManagerState;


@end


@implementation BLECentralManager

#pragma mark -- init

static BLECentralManager *instance;

+ (BLECentralManager *)sharedManager{
    if (instance == nil) {
        instance = [[self alloc] init];
    }
    return instance;
}

+ (id)allocWithZone:(NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}

-(id)init{
    self = [super init];
    if (self) {
        _activeCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
        
        _delegates = [NSMutableArray array];
        _peripherals = [NSMutableArray array];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_updataShowStringBuffer:) name:@"CBUpdataShowStringBuffer" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_peripheralStateChange:) name:@"CBPeripheralStateChange" object:nil];
        
        NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docDir = documents[0];
        NSString *archivePath = [docDir stringByAppendingPathComponent:ARCHIVE_PATH_RENAME];
        _identifierAndAliasDic = (NSMutableDictionary *)[NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
        if (!_identifierAndAliasDic) {
            _identifierAndAliasDic = [NSMutableDictionary dictionary];
        }
    }
    return self;
}

#pragma mark -- public methods

-(BOOL)startScanning{
    if([BLECentralManager sharedManager].currentCentralManagerState == BLECentralDelegateStateCentralManagerPoweredOff){
        if (_enbaleLog) {
            NSLog(@"start scanning failed,because current central manager state is poweroff");
        }
        return NO;
    }
    
    [_activeCentralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];
    
    return YES;
}

-(void)stopScanning{
    [_activeCentralManager stopScan];
}

-(BOOL)resetScanning{
    //删掉蓝牙扫描器中的所有非连接设备
    [self.peripherals removeAllObjects];
    if (_activePeripheral) {
        [self.peripherals addObject:_activePeripheral];
    }
    
    [self stopScanning];
    
    return [self startScanning];
}

-(void)addDelegate:(id<BLEControllerDelegate>)delegate{
    if(![_delegates containsObject:delegate]){
        [_delegates addObject:delegate];
    }
}

-(void)removeDelegate:(id<BLEControllerDelegate>)delegate{
    if([_delegates containsObject:delegate]){
        [_delegates removeObject:delegate];
    }
}

-(void)connectPeripheral:(CBPeripheral*)peripheral{
    if (peripheral && peripheral.state!=CBPeripheralStateConnected){
        [_activeCentralManager connectPeripheral:peripheral options:nil];
    }
}

-(void)disconnectPeripheral:(CBPeripheral*)peripheral{
    if(peripheral){
        [_activeCentralManager stopScan];
        [_activeCentralManager cancelPeripheralConnection:peripheral];
    }
}

-(void)disconnectActivePeripheral{
    if (!_activePeripheral) {
        return;
    }
    
    [self disconnectPeripheral:_activePeripheral.activePeripheral];
    [_activePeripheral disconnectActivePeripheral];
    
    self.currentCentralManagerState = BLECentralDelegateStateDisconnectPeripheral;
    [self _peripheralDisconnected];
}

-(void)sendData:(NSData *)data{
    [_activePeripheral sendData:data];
}

-(void)sendDataMandatory:(NSData *)data{
    [_activePeripheral sendDataMandatory:data];
}

-(void)setAirblockGPIO:(NSInteger)gpio isHighLevel:(BOOL)isHighLevel{
    [_activePeripheral setAirblockGPIO:gpio isHighLevel:isHighLevel];
}

-(void)setAirblockEscapeCheck:(BOOL)escape{
    [_activePeripheral setAirblockEscapeCheck:escape];
}

- (void)setCommonGPIO:(NSInteger)gpio isHigh:(BOOL)isHigh{
    [_activePeripheral setCommonGPIO:gpio isHigh:isHigh];
}

-(void)renameWithIdentifier:(NSString *)identifier andAlias:(NSString *)alias {
    NSString *mAlias = [_identifierAndAliasDic valueForKey:identifier];
    if (!mAlias && _enbaleLog){
        NSLog(@"首次归档");
    }
    if ([alias isEqualToString:mAlias]) {
        if (_enbaleLog) {
            NSLog(@"与原alias一样，return");
        }
        return;
    }
    
    [_identifierAndAliasDic setObject:alias forKey:identifier];
    if (_enbaleLog) {
        NSLog(@"执行归档 self.identifierAndAliasDic=%@",_identifierAndAliasDic);
    }
    NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = documents[0];
    NSString *archivePath = [docDir stringByAppendingPathComponent:ARCHIVE_PATH_RENAME];
    [NSKeyedArchiver archiveRootObject:_identifierAndAliasDic toFile:archivePath];
}

-(NSString *)getAliasWithIdentifier:(NSString *)identifier {
    return (NSString *)[_identifierAndAliasDic objectForKey:identifier];
}

#pragma mark -- private methods

/**
 *  通知代理状态已改变
 */
-(void)_stateChanged:(NSUInteger)state{
    for (id<BLEControllerDelegate> delegate in _delegates) {
        if([delegate respondsToSelector:@selector(bleStateChanged:)]){
            [delegate bleStateChanged:state];
        }
    }
}

/**
 *  收到数据通知
 *
 *  @param notification 通知
 */
-(void)_updataShowStringBuffer:(NSNotification*)notification{
    [self _receivedData:[notification.userInfo objectForKey:@"data"]];
}

/**
 *  收到数据
 *
 *  @param data 数据
 */
-(void)_receivedData:(NSData*)data{
    if (_isBootloader) {//bootloader模式下，直接发送不用处理数据
        for (id<BLEControllerDelegate> delegate in _delegates) {
            if([delegate respondsToSelector:@selector(bleReceivedData:)]){
                [delegate bleReceivedData:data];
            }
        }
        
    }else{
        //加工数据代理加工完数据后再发送出去
        NSData* processData = data;
        if (_processDataDelegate && [_processDataDelegate respondsToSelector:@selector(processData:)]) {
            processData = [_processDataDelegate processData:data];
        }
        
        //若加工回来的数据为空，表示不发送该数据
        if (!processData) {
            return;
        }
        
        if (_enbaleLog) {
            NSLog(@"Receive Data: %@", processData);
            NSString *dataInAscii = [[NSString alloc] initWithData:processData encoding:NSUTF8StringEncoding];
            NSLog(@"dataInAscii: %@", dataInAscii);
        }
        
        for (id<BLEControllerDelegate> delegate in _delegates) {
            if([delegate respondsToSelector:@selector(bleReceivedData:)]){
                [delegate bleReceivedData:processData];
            }
        }
    }
}

/**
 *  通知代理外部设备信号已改变
 */
-(void)_peripheralRSSIChange:(BLEPeripheral *)peripheral{
    for (id<BLEControllerDelegate> delegate in _delegates) {
        if([delegate respondsToSelector:@selector(blePeripheralRSSIChange:)]){
            [delegate blePeripheralRSSIChange:peripheral];
        }
    }
}

/**
 *  收到状态改变通知
 *
 *  @param notification 通知
 */
-(void)_peripheralStateChange:(NSNotification*)notification{
    if(_activePeripheral.currentPeripheralState==BLEPeripheralDelegateStateKeepActive){
        [self _peripheralConnected];
    }
}

/**
 *  外围设备已连接
 */
-(void)_peripheralConnected{
    if (!_delegates || _delegates.count < 1) {
        return;
    }
    
    for (id<BLEControllerDelegate> delegate in _delegates) {
        if([delegate respondsToSelector:@selector(bleConnected)]){
            [delegate bleConnected];
        }
    }
}

/**
 *  外围设备断开连接
 */
-(void)_peripheralDisconnected{
    self.activePeripheral = nil;
    
    if (!_delegates || _delegates.count < 1) {
        return;
    }
    
    for (int i=0;i<_delegates.count;i++) {
        id delegate = [_delegates objectAtIndex:i];
        if([delegate respondsToSelector:@selector(bleDisconnected)]){
            [delegate bleDisconnected];
        }
    }
}

/**
 *  是否包含外部设备
 *
 *  @param peripheral 外部设备
 *
 *  @return 有包含--返回包含的BLEPeripheral 没有包含--返回nil
 */
-(BLEPeripheral *)_containPeripheral:(CBPeripheral *)peripheral{
    for (BLEPeripheral* bean in _peripherals) {
        if ([[bean.activePeripheral.identifier UUIDString] isEqualToString:[peripheral.identifier UUIDString]]) {
            return bean;
        }
    }
    return nil;
}


#pragma mark -- CBCentralManagerDelegate

/**
 *  中心设备管理器状态改变
 *
 *  @param central 中心设备管理器
 */
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
    if(_activeCentralManager != central) return;
    
    switch ([central state]){
        case CBCentralManagerStatePoweredOff:{//掉电状态
            self.currentCentralManagerState = BLECentralDelegateStateCentralManagerPoweredOff;
            self.activePeripheral = nil;
            [self _stateChanged:self.currentCentralManagerState];
            [self resetScanning];
            break;
        }
        case CBCentralManagerStateUnauthorized:{//未经授权的状态
            self.currentCentralManagerState = BLECentralDelegateStateCentralManagerUnauthorized;
            [self _stateChanged:self.currentCentralManagerState];
            [self resetScanning];
            break;
        }
        case CBCentralManagerStateUnknown:{// 未知状态
            self.currentCentralManagerState = BLECentralDelegateStateCentralManagerUnknown;
            [self _stateChanged:self.currentCentralManagerState];
            [self resetScanning];
            break;
        }
        case CBCentralManagerStateUnsupported:{//不支持
            self.currentCentralManagerState = BLECentralDelegateStateCentralManagerUnsupported;
            [self _stateChanged:self.currentCentralManagerState];
            [self resetScanning];
            break;
        }
        case CBCentralManagerStatePoweredOn:{//上电状态
            self.currentCentralManagerState = BLECentralDelegateStateCentralManagerPoweredOn;
            [self _stateChanged:self.currentCentralManagerState];
            [self startScanning];
            break;
        }
        case CBCentralManagerStateResetting:{//重置状态
            self.currentCentralManagerState = BLECentralDelegateStateCentralManagerResetting;
            [self _stateChanged:self.currentCentralManagerState];
            [self resetScanning];
            break;
        }
    }
}

/**
 *  中心设备扫描到外围设备
 *
 *  @param central           中心设备管理器
 *  @param peripheral        外围设备
 *  @param advertisementData 数据
 *  @param RSSI              距离
 */
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
    if(_activeCentralManager != central) return;
    
    BLEPeripheral* containBean = [self _containPeripheral:peripheral];
    if(!containBean){
        //创建bean
        BLEPeripheral* bean = [[BLEPeripheral alloc]initWithPeripheral:peripheral];
        bean.delegate = self;
        bean.RSSI = RSSI;
        if (!peripheral.name) {
            bean.alias = @"Unkonwn";
        }else{
            bean.alias = peripheral.name;
        }
        if ([self getAliasWithIdentifier:[peripheral.identifier UUIDString]]) {
            bean.alias = [self getAliasWithIdentifier:[peripheral.identifier UUIDString]];
        }
        
        //加入数组
        [_peripherals addObject:bean];
        
        self.currentCentralManagerState = BLECentralDelegateStateDiscoverPeripheral;
        [self _stateChanged:self.currentCentralManagerState];
        
    }else{
        if ([containBean.RSSI integerValue] != [RSSI integerValue]) {
            containBean.RSSI = RSSI;
            
            self.currentCentralManagerState = BLECentralDelegateStatePeripheralRSSIChange;
            [self _stateChanged:self.currentCentralManagerState];
            
            [self _peripheralRSSIChange:containBean];
        }
    }
}

/**
 *  中心设备连接外围设备
 *
 *  @param central    中心设备
 *  @param peripheral 外围设备
 */
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    if(_activeCentralManager != central) return;
    
    BLEPeripheral* bean = [self _containPeripheral:peripheral];
    if(!bean && _enbaleLog){
        NSLog(@"has no contain this peripheral");
    }
    self.activePeripheral = bean;
    self.currentConnectedDeviceName = bean.displayName;
    
    //开始扫描服务
    [_activePeripheral startPeripheral:peripheral];
    
    self.currentCentralManagerState = BLECentralDelegateStateConnectPeripheral;
    [self _stateChanged:self.currentCentralManagerState];
}


/**
 *  断开连接
 *
 *  @param central    中心设备
 *  @param peripheral 外设
 *  @param error      错误
 */
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    //!!!!出现iPhone回调该方法，但是iPad不回调的问题，故这里的操作在disconnectActivePeripheral方法中也需要
    if(_activeCentralManager != central) return;
    
    self.currentCentralManagerState = BLECentralDelegateStateDisconnectPeripheral;
    [self _peripheralDisconnected];
}

/**
 *  ！！！ios8.4后已被废弃
 *  中心设备连接检索到的外围设备
 *
 *  @param central     中心设备
 *  @param peripherals 外围设备
 */
-(void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals{
    if ([_activeCentralManager isEqual:central]) {
        for (CBPeripheral *aPeripheral in peripherals){
            BLEPeripheral* bean = [self _containPeripheral:aPeripheral];
            if(!bean && _enbaleLog){
                NSLog(@"has no contain this peripheral");
            }
            [central connectPeripheral:aPeripheral options:nil];
        }
        
        self.currentCentralManagerState = BLECentralDelegateStateRetrieveConnectedPeripherals;
        [self _stateChanged:self.currentCentralManagerState];
    }
}


#pragma mark -- BLEPeripheralDelegate

-(void)didPeripheral:(CBPeripheral *)peripheral updateRSSI:(NSNumber *)RSSI{
    BLEPeripheral* containBean = [self _containPeripheral:peripheral];
    
    if ([containBean.RSSI integerValue] != [RSSI integerValue]) {
        containBean.RSSI = RSSI;
        self.currentCentralManagerState = BLECentralDelegateStatePeripheralRSSIChange;
        [self _peripheralRSSIChange:containBean];
    }
}

@end
